/*************************************************************************
 *
 * MenuTask
 * 
 * (c) Ales Povalac, alpov@alpov.net, http://www.alpov.net/
 * 
 *************************************************************************
 *
 * $Id: menu.c 33 2006-07-01 09:19:05Z alpov $
 * 
 *************************************************************************/         

#define _MENU_C
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
#include "../avrx/avrx.h"
#include "../avrx/AvrXFifo.h"
#include "beep.h"
#include "button.h"
#include "menu.h"
#include "menu_def.h"
#include "lcd.h"
#include "menu_lcd.h"
#include "data.h"
#include "datetime.h"
#include "locate.h"

AVRX_DECL_FIFO(MenuFifo, 16);

static signed char *value;
static unsigned long *clock;
static signed char value_min, value_max;
static unsigned char value_overlap = 0;

static unsigned char substate = 0;

static char PROGMEM dt_min[6] = {0, 1, 0, 0, 0, 0};
static char PROGMEM dt_max[6] = {127, 12, 130, 23, 59, 59};


/*************************************************************************
 * inicializace tasku
 *************************************************************************/ 
void InitMenuTask(void)
{
    // inicializace ulohy
    AVRX_INIT_FIFO(MenuFifo);
    
    //inicializace displeje
    lcd_init(LCD_DISP_ON);
    LcdInitHiLevel();

    AvrXPutFifo(MenuFifo, MENU_REFRESH);
}


/*************************************************************************
 * refresh menu
 * interni funkce volana MenuTaskem
 * pro externi refresh: AvrXPutFifo(MenuFifo, MENU_REFRESH); 
 *************************************************************************/ 
void MenuInternalRefresh(unsigned char state)
{
    lcd_clrscr();
    lcd_command(LCD_DISP_ON); 
    switch (state) {
        case ST_MAIN: {
            unsigned char i = idata.sensor_index;
            
            printf_P(PSTR(MSG_MAIN), 
                sdata.sensor_desc[i], 
                sdata.temperature[i] / 10, 
                abs(sdata.temperature[i] % 10)
            );
            break;
        }

        case ST_SHIFT:
        case ST_SHIFT_EDIT: {
            if (sdata.shift_length) {
                printf_P(PSTR(MSG_SHIFT_TIMED),
                    sdata.shift,
                    sdata.shift_length
                );
            } else {
                printf_P(PSTR(MSG_SHIFT_STATIC),
                    sdata.shift
                );
            }
            break;
        }

        case ST_MODE:
        case ST_MODE_EDIT: {
            printf_P(PSTR(MSG_MODE),
                sdata.mode_desc[sdata.mode]
            );
            break;
        }

        case ST_RTC:
        case ST_RTC_EDIT: {
            unsigned char year, month, day, hour, min, sec;
        
            decode_date(sdata.now, &year, &month, &day);
            decode_time(sdata.now, &hour, &min, &sec);
            printf_P(PSTR(MSG_RTC),
                day,
                month,
                year,
                hour,
                min
            );
            break;
        }

        case ST_SWITCH:
        case ST_SWITCH_EDIT_M:
        case ST_SWITCH_EDIT_C: {
            unsigned char year, month, day, hour, min, sec;
        
            decode_date(sdata.sw_datetime, &year, &month, &day);
            decode_time(sdata.sw_datetime, &hour, &min, &sec);
            printf_P(PSTR(MSG_SWITCH),
                sdata.mode_desc[sdata.sw_mode],
                day,
                month,
                year,
                hour,
                min
            );
            break;
        }

        case ST_CONFIG:
        case ST_CONFIG_EDIT: {
            printf_P(PSTR(MSG_CONFIG),
                idata.id,
                idata.contrast
            );
            break;
        }

        case ST_MESSAGE: {
            printf_P(PSTR(MSG_MESSAGE),
                sdata.message
            );
            break;
        }

        default:
            AvrXHalt();
    }
    lcd_gotoxy(15, 0);
    lcd_command(
        ((state == ST_SHIFT_EDIT) || (state == ST_MODE_EDIT) || (state == ST_RTC_EDIT) || (state == ST_SWITCH_EDIT_M) ||
         (state == ST_SWITCH_EDIT_C) || (state == ST_CONFIG_EDIT) || (state == ST_MESSAGE)
        ) ? LCD_DISP_ON_CURSOR_BLINK : LCD_DISP_ON
    );
}


/*************************************************************************
 * uloha MenuTask
 * min. stack 93B 
 *************************************************************************/ 
AVRX_GCC_TASKDEF(MenuTask, 200, 3) // FIXME stack 100B
{
    signed char c;
    unsigned char state = ST_MAIN;
    unsigned char i, j, key;
    unsigned char (*pStateFunc)(unsigned char key, unsigned char state);

    while (1) {
        c = AvrXWaitPullFifo(MenuFifo);
        if (c < 0) {
            // specialni pozadavek
            if (c == MENU_REFRESH)
                ;
            else if (c == MENU_MESSAGE)
                state = ST_MESSAGE;
            else
                AvrXHalt();
            
        } else {
            // stisknuta klavesa - obsluha stavovym automatem
            for (i = 0; (j = pgm_read_byte(&menu_nextstate[i].state)); i++) {
                key = pgm_read_byte(&menu_nextstate[i].key);
                if ((j == state) && (key & c)) {
                    pStateFunc = (PGM_VOID_P)pgm_read_word(&menu_nextstate[i].pStateFunc);
                    if (pStateFunc) {
                        state = pStateFunc(c, state);                    
                    } else {
                        state = pgm_read_byte(&menu_nextstate[i].nextstate);
                    }
                    break;
                }
            }

        }
        MenuInternalRefresh(state);
    }
}


/*************************************************************************
 * stavova funkce - zmena senzoru na hlavni obrazovce
 *************************************************************************/ 
unsigned char MenuChangeSensor(unsigned char key, unsigned char state)
{
    value = &idata.sensor_index;
    value_min = 0;
    value_max = sdata.sensor_count - 1;
    value_overlap = 1;
    
    return MenuChangeValue(key, state);
}


/*************************************************************************
 * stavova funkce - uprava obecne hodnoty
 *************************************************************************/ 
unsigned char MenuChangeValue(unsigned char key, unsigned char state)
{
    if (key == KEY_PLUS) {
        if (*value < value_max)
            (*value)++;
        else if (value_overlap)
            (*value) = value_min;
            
    } else if (key == KEY_MINUS) {
        if (*value > value_min)
            (*value)--;
        else if (value_overlap)
            (*value) = value_max;

    }
    
    // specialni pripad - kontrast ihned ukazovat
    if (value == &idata.contrast)
        LcdSetContrast(idata.contrast);
    
    return state;
}


/*************************************************************************
 * stavova funkce - uprava hodnoty hodin
 *************************************************************************/ 
unsigned char MenuChangeClock(unsigned char key, unsigned char state)
{
    unsigned char dt[6];
    
    decode_date(*clock, &dt[2], &dt[1], &dt[0]);
    decode_time(*clock, &dt[3], &dt[4], &dt[5]);

    if (key == KEY_PLUS) {
        if (dt[substate] < pgm_read_byte(&dt_max[substate]))
            dt[substate]++;
            
    } else if (key == KEY_MINUS) {
        if (dt[substate] > pgm_read_byte(&dt_min[substate]))
            dt[substate]--;

    }
    
    *clock = encode_datetime(dt[2], dt[1], dt[0], dt[3], dt[4], 0);
    
    return state;
}


/*************************************************************************
 * stavova funkce - posun
 *************************************************************************/ 
unsigned char MenuEditShift(unsigned char key, unsigned char state)
{
    if (state == ST_SHIFT) {
        BeginEditing();
        substate = 0;
        value = &sdata.shift;
        value_min = -20;
        value_max = 20;
        value_overlap = 0;
        return ST_SHIFT_EDIT;
    }
    if (key == KEY_FN) { 
        CancelEditing();
        return ST_SHIFT;
    }

    switch (++substate) {
        case 1: {
            value = &sdata.shift_length;
            value_min = 0;
            value_max = 30;
            return ST_SHIFT_EDIT;
        }
        case 2: {
            EndEditing();
            return ST_SHIFT;
        }
        default:
            return ST_ERROR;
    }
}


/*************************************************************************
 * stavova funkce - rezim
 *************************************************************************/ 
unsigned char MenuEditMode(unsigned char key, unsigned char state)
{
    if (state == ST_MODE) {
        BeginEditing();
        value = &sdata.mode;
        value_min = 0;
        value_max = sdata.mode_count - 1;
        value_overlap = 1;
        return ST_MODE_EDIT;
    }
    if (key == KEY_FN) {
        CancelEditing();
        return ST_MODE;
    }

    EndEditing();
    return ST_MODE;
}


/*************************************************************************
 * stavova funkce - hodiny realneho casu
 *************************************************************************/ 
unsigned char MenuEditRtc(unsigned char key, unsigned char state)
{
    if (state == ST_RTC) {
        BeginEditing();
        substate = 0;
        clock = &sdata.now;
        return ST_RTC_EDIT;
    }
    if (key == KEY_FN) {
        CancelEditing();
        return ST_RTC;
    }

    switch (++substate) {
        case 1:
        case 2:
        case 3:
        case 4: {
            return ST_RTC_EDIT;
        }
        case 5: {
            EndEditing();
            return ST_RTC;
        }
        default:
            return ST_ERROR;
    }
}


/*************************************************************************
 * stavova funkce - prepnuti rezimu
 *************************************************************************/ 
unsigned char MenuEditSwitch(unsigned char key, unsigned char state)
{
    if (state == ST_SWITCH) {
        BeginEditing();
        substate = 0;
        value = &sdata.sw_mode;
        value_min = 0;
        value_max = sdata.mode_count - 1;
        value_overlap = 1;
        return ST_SWITCH_EDIT_M;
    }
    if (key == KEY_FN) {
        CancelEditing();
        return ST_SWITCH;
    }
    
    if (state == ST_SWITCH_EDIT_M) {
        substate = 0;
        clock = &sdata.sw_datetime;
        return ST_SWITCH_EDIT_C;

    } else {  // ST_SWITCH_MODE_C
        switch (++substate) {
            case 1:
            case 2:
            case 3:
            case 4: {
                return ST_SWITCH_EDIT_C;
            }
            case 5: {
                EndEditing();
                return ST_SWITCH;
            }
            default:
                return ST_ERROR;
        }

    }
}


/*************************************************************************
 * stavova funkce - konfigurace jednotky
 *************************************************************************/ 
unsigned char MenuEditConfig(unsigned char key, unsigned char state)
{
    if (state == ST_CONFIG) {
        substate = 0;
        value = &idata.id;
        value_min = 0;
        value_max = 127;
        value_overlap = 1;
        return ST_CONFIG_EDIT;
    }
    if (key == KEY_FN) {
        ReadEepromData();
        return ST_CONFIG;
    }

    switch (++substate) {
        case 1: {
            value = &idata.contrast;
            value_min = 0;
            value_max = 63;
            value_overlap = 0;
            return ST_CONFIG_EDIT;
        }
        case 2: {
            WriteEepromData();
            return ST_CONFIG;
        }
        default:
            return ST_ERROR;
    }
}

